////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   ObjectManager.cpp
//  Version:     v1.00
//  Created:     10/10/2001 by Timur.
//  Compilers:   Visual C++ 6.0
//  Description: ObjectManager implementation.
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ObjectManager.h"

#include "..\DisplaySettings.h"

#include "TagPoint.h"
#include "TagComment.h"
//#include "StatObj.h"
#include "EntityObject.h"
#include "AIWave.h"
#include "Group.h"
#include "Volume.h"
#include "SoundObject.h"
#include "ShapeObject.h"
#include "AIPoint.h"
#include "BrushObject.h"
#include "CloudObject.h"
#include "CloudGroup.h"
#include "LightPropagationVolumeObject.h"
#include "VoxelObject.h"
#include "DecalObject.h"
#include "DistanceCloudObject.h"
#include "GravityVolumeObject.h"
#include "Vehicles\VehiclePrototype.h"
#include "Vehicles\VehicleHelperObject.h"
#include "Vehicles\VehiclePart.h"
#include "Vehicles\VehicleSeat.h"
#include "Vehicles\VehicleWeapon.h"
#include "Vehicles\VehicleComp.h"
#include "SmartObjectHelperObject.h"
#include "AutoCubemapObject.h"

#include "CameraObject.h"
#include "AIAnchor.h"
#include "SmartObject.h"
#include "AreaBox.h"
#include "LightBox.h"
#include "AreaSphere.h"
#include "WaterShapeObject.h"
#include "VisAreaShapeObject.h"
#include "ProtEntityObject.h"
#include "PrefabObject.h"
#include "PrefabBuildingObject.h"
#include "Brush\SolidBrushObject.h"
#include "RoadObject.h"
#include "RiverObject.h"
#include "WaterWaveObject.h"
#include "SequenceObject.h"
#include "RopeObject.h"
#include "MeshObject.h"
#include "CharAttachHelper.h"
#include "Light.h"
#include "EnvironmentProbeObject.h"
#include "AICoverSurface.h"
#include "RefPicture.h"

#include "Viewport.h"

#include "GizmoManager.h"
#include "ObjectLayerManager.h"
#include "AxisGizmo.h"

#include "ObjectPhysicsManager.h"

#include "EditMode\ObjectMode.h"

#include "IAgent.h"
#include <IEntitySystem.h>
#include <io.h>

#include "Console/ConsoleSync.h"
#include "SegmentedWorld/SegmentedWorldManager.h"
#include "CustomMessageBox.h"

#include "ProceduralSystem/ProceduralCreation.h"
#include "Geometry\EdMesh.h"
#include "HelpLink.h"

#include "Material\MaterialManager.h"

#include "IAIObject.h"
#include "../EditMode/DeepSelection.h"
#include "Objects\EnvironmentProbeObject.h"
#include "HyperGraph\FlowGraphManager.h"
#include "PickObjectTool.h"

/*!
 *	Class Description used for object templates.
 *	This description filled from Xml template files.
 */
class CXMLObjectClassDesc : public CObjectClassDesc
{
public:
	CObjectClassDesc*	superType;
	CString type;
	CString category;
	CString fileSpec;
	GUID guid;
	
public:
	REFGUID ClassID()
	{
		return guid;
	}
	ObjectType GetObjectType() { return superType->GetObjectType(); };
	const char* ClassName() { return type; };
	const char* Category() { return category; };
	CRuntimeClass* GetRuntimeClass() { return superType->GetRuntimeClass(); };
	const char* GetFileSpec()
	{
		if (!fileSpec.IsEmpty())
			return fileSpec;
		else
			return superType->GetFileSpec();
	};
	virtual int GameCreationOrder() { return superType->GameCreationOrder(); };
};

//////////////////////////////////////////////////////////////////////////
//! Undo New Object
CUndoBaseObjectNew::CUndoBaseObjectNew( CBaseObject *obj )
{
	m_object = obj;
}

// Return size of xml state.
int CUndoBaseObjectNew::GetSize()
{
	return sizeof(*this);
}

const char* CUndoBaseObjectNew::GetDescription()
{
	return "New BaseObject";
}

const char* CUndoBaseObjectNew::GetObjectName()
{
	return m_object->GetName(); 
}

void CUndoBaseObjectNew::Undo( bool bUndo )
{
	if (bUndo)
	{
		SW_TEST_OBJ_MOD(m_object, true, false);
		SW_ON_OBJ_DEL(m_object);
	}

	if (bUndo)
	{
		m_redo = XmlHelpers::CreateXmlNode("Redo");
		// Save current object state.
		CObjectArchive ar(GetIEditor()->GetObjectManager(),m_redo,false);
		ar.bUndo = true;
		m_object->Serialize( ar );
		m_object->SetLayerModified();
	}
	// Delete this object.
	GetIEditor()->DeleteObject( m_object );
}

void CUndoBaseObjectNew::Redo()
{
	if (m_redo)
	{
		IObjectManager *pObjMan = GetIEditor()->GetObjectManager();
		{
			CObjectArchive ar( pObjMan,m_redo,true );
			ar.bUndo = true;
			ar.LoadObject( m_redo,m_object );
		}
		pObjMan->SelectObject( m_object );
		m_object->SetLayerModified();
		SW_ON_OBJ_NEW(m_object);
	}
}


//////////////////////////////////////////////////////////////////////////
//! Undo Delete Object
class CUndoBaseObjectDelete : public IUndoObject
{
public:
	CUndoBaseObjectDelete( CBaseObject *obj )
	{
		m_object = obj;
		// Save current object state.
		m_undo = XmlHelpers::CreateXmlNode("Undo");
		CObjectArchive ar(GetIEditor()->GetObjectManager(),m_undo,false);
		ar.bUndo = true;
		m_object->Serialize( ar );
		m_object->SetLayerModified();
	}
protected:
	virtual int GetSize() { return sizeof(*this); }; // Return size of xml state.
	virtual const char* GetDescription() { return "Delete BaseObject"; };
	virtual const char* GetObjectName(){ return m_object->GetName(); };

	virtual void Undo( bool bUndo )
	{
		IObjectManager *pObjMan = GetIEditor()->GetObjectManager();
		CObjectArchive ar( pObjMan,m_undo,true );
		ar.bUndo = true;
		ar.LoadObject( m_undo,m_object );
		m_object->ClearFlags(OBJFLAG_SELECTED);
		pObjMan->SelectObject( m_object );
		m_object->SetLayerModified();
		GetIEditor()->GetConsoleSync()->OnObjectModified( m_object, false, false );
	}
	virtual void Redo()
	{
		// Delete this object.
		m_object->SetLayerModified();
		GetIEditor()->GetConsoleSync()->OnObjectModified( m_object, true, false );
		GetIEditor()->DeleteObject( m_object );
	}

private:
	CBaseObjectPtr m_object;
	XmlNodeRef m_undo;
};

//////////////////////////////////////////////////////////////////////////
//! Undo Select Object
class CUndoBaseObjectSelect : public IUndoObject
{
public:
	CUndoBaseObjectSelect( CBaseObject *obj )
	{
		assert( obj != 0 );
		m_object = obj;
		m_bUndoSelect = obj->IsSelected();
	}
protected:
	virtual void Release() { delete this; };
	virtual int GetSize() { return sizeof(*this); }; // Return size of xml state.
	virtual const char* GetDescription() { return "Select Object"; };
	virtual const char* GetObjectName(){ return m_object->GetName(); };

	virtual void Undo( bool bUndo )
	{
		if (bUndo)
		{
			m_bRedoSelect = m_object->IsSelected();
		}
		if (m_bUndoSelect)
			GetIEditor()->GetObjectManager()->SelectObject(m_object);
		else
			GetIEditor()->GetObjectManager()->UnselectObject(m_object);
	}
	virtual void Redo()
	{
		if (m_bRedoSelect)
			GetIEditor()->GetObjectManager()->SelectObject(m_object);
		else
			GetIEditor()->GetObjectManager()->UnselectObject(m_object);
	}

private:
	CBaseObjectPtr m_object;
	bool m_bUndoSelect;
	bool m_bRedoSelect;
};

//////////////////////////////////////////////////////////////////////////
// CObjectManager implementation.
//////////////////////////////////////////////////////////////////////////
CObjectManager* g_pObjectManager = 0;

//////////////////////////////////////////////////////////////////////////
CObjectManager::CObjectManager()
{
	g_pObjectManager = this;
	m_currSelection = &m_defaultSelection;
	m_currEditObject = 0;

	m_selectCallback = 0;
	m_createGameObjects = true;
	m_bSingleSelection = false;
	m_nLastSelCount=0;

	m_bGenUniqObjectNames = true;

	m_bSelectionChanged = false;
	m_bVisibleObjectValid = true;
	m_lastHideMask = 0;

	m_pLoadProgress = 0;
	m_totalObjectsToLoad = 0;
	m_loadedObjects = 0;

	m_bSkipMessages = false;
	m_bExiting = false;

	m_isUpdateVisibilityList = false;

	m_maxObjectViewDistRatio = 0.00001f;

	// Creates objects layers manager.
	m_pLayerManager = new CObjectLayerManager( this );

	// Creates gizmo manager.
	m_gizmoManager = new CGizmoManager;

	m_pPhysicsManager = new CObjectPhysicsManager;

	RegisterObjectClasses();

	LoadRegistry();
}

//////////////////////////////////////////////////////////////////////////
CObjectManager::~CObjectManager()
{
	m_bExiting = true;
	SaveRegistry();
	DeleteAllObjects();

	if (m_gizmoManager)
		delete m_gizmoManager;

	if (m_pLayerManager)
		delete m_pLayerManager;

	delete m_pPhysicsManager;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RegisterObjectClasses()
{
	// Register default classes.
	CClassFactory *cf = CClassFactory::Instance();
	cf->RegisterClass( new CTagPointClassDesc );
	cf->RegisterClass( new CNavVolumeSeedClassDesc );
	//cf->RegisterClass( new CRespawnPointClassDesc );
	//cf->RegisterClass( new CSpawnPointClassDesc );
	cf->RegisterClass( new CTagCommentClassDesc );
	//cf->RegisterClass( new CStaticObjectClassDesc );
	//cf->RegisterClass( new CBuildingClassDesc );
	cf->RegisterClass( new CLightPropagationVolumeObjectClassDesc );	
	cf->RegisterClass( new CEntityClassDesc );
	cf->RegisterClass( new CSimpleEntityClassDesc );
	cf->RegisterClass( new CGeomEntityClassDesc );
	cf->RegisterClass( new CGroupClassDesc );
	cf->RegisterClass( new CVolumeClassDesc );
	cf->RegisterClass( new CSoundObjectClassDesc );
	cf->RegisterClass( new CShapeObjectClassDesc );
	cf->RegisterClass( new CLightShapeObjectClassDesc );
	cf->RegisterClass( new CAIPathObjectClassDesc );
	cf->RegisterClass( new CAIShapeObjectClassDesc );
	cf->RegisterClass( new CAIForbiddenAreaObjectClassDesc );
	cf->RegisterClass( new CAIForbiddenBoundaryObjectClassDesc );
	cf->RegisterClass( new CAINavigationModifierObjectClassDesc );
	cf->RegisterClass( new CAIOcclusionPlaneObjectClassDesc );
	cf->RegisterClass( new CAIPerceptionModifierObjectClassDesc );
	cf->RegisterClass( new CNavigationAreaObjectDesc );
	cf->RegisterClass( new CAITerritoryObjectClassDesc );
	cf->RegisterClass( new CAIWaveObjectClassDesc );
	cf->RegisterClass( new CAIPointClassDesc );
	cf->RegisterClass( new CAICoverSurfaceClassDesc );
	//cf->RegisterClass( new CAIStreamingAreaObjectClassDesc );
	cf->RegisterClass( new CBrushObjectClassDesc );
	cf->RegisterClass( new CCameraObjectClassDesc );
	cf->RegisterClass( new CCameraObjectTargetClassDesc );
	cf->RegisterClass( new CAIAnchorClassDesc );
	cf->RegisterClass( new CSmartObjectClassDesc );
	cf->RegisterClass( new CAreaBoxClassDesc );
	cf->RegisterClass( new CLightBoxClassDesc );
	cf->RegisterClass( new CAreaSphereClassDesc );
	cf->RegisterClass( new CWaterShapeObjectClassDesc );
	cf->RegisterClass( new CVisAreaShapeObjectClassDesc );
	cf->RegisterClass( new CPortalShapeObjectClassDesc );
	cf->RegisterClass( new COccluderShapeObjectClassDesc );
	cf->RegisterClass( new COccluderPlaneObjectClassDesc );
	cf->RegisterClass( new CProtEntityObjectClassDesc );
	cf->RegisterClass( new CPrefabObjectClassDesc );
	cf->RegisterClass( new CPrefabBuildingObjectClassDesc );
	cf->RegisterClass( new CSolidBrushObjectClassDesc );
	cf->RegisterClass( new CCloudObjectClassDesc );
	cf->RegisterClass( new CCloudGroupClassDesc );
	cf->RegisterClass( new CVoxelObjectClassDesc );
	cf->RegisterClass( new CVehiclePrototypeClassDesc );
	cf->RegisterClass( new CVehicleHelperClassDesc );  
	cf->RegisterClass( new CVehiclePartClassDesc );
	cf->RegisterClass( new CVehicleSeatClassDesc );
	cf->RegisterClass( new CVehicleWeaponClassDesc );
	cf->RegisterClass( new CVehicleComponentClassDesc );
	cf->RegisterClass( new CRoadObjectClassDesc );
	cf->RegisterClass( new CRiverObjectClassDesc );  
	cf->RegisterClass( new CSmartObjectHelperClassDesc );
	cf->RegisterClass( new CDecalObjectClassDesc );
	cf->RegisterClass( new CSequenceObjectClassDesc );
	cf->RegisterClass( new CGravityVolumeObjectClassDesc );  
	cf->RegisterClass( new CWaterWaveObjectClassDesc );
	cf->RegisterClass( new CDistanceCloudObjectClassDesc );
	cf->RegisterClass( new CAutoCubemapObjectClassDesc );
	cf->RegisterClass( new CRopeObjectClassDesc );
	cf->RegisterClass( new CMeshObjectClassDesc );
	cf->RegisterClass( new CCharacterAttachHelperObjectClassDesc );
	cf->RegisterClass( new CEnvironmentProbeObjectClassDesc );
//	cf->RegisterClass( new CLightClassDesc );
	cf->RegisterClass( new CRefPictureClassDesc );
	cf->RegisterClass( new CNavigationSeedPointClassDesc );
  
	LoadRegistry();
}

//////////////////////////////////////////////////////////////////////////
void	CObjectManager::SaveRegistry()
{

}

void	CObjectManager::LoadRegistry()
{
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::NewObject( CObjectClassDesc *cls,CBaseObject *prev,const CString &file )
{
	ASSERT( cls != 0 );
	CRuntimeClass *rtClass = cls->GetRuntimeClass();
	ASSERT( rtClass->IsDerivedFrom(RUNTIME_CLASS(CBaseObject)) );
	if (prev)
	{
		// Both current and previous object must be of same type.
		ASSERT( cls == prev->GetClassDesc() );
	}

	// Suspend undo operations when initializing object.
	GetIEditor()->SuspendUndo();

	CBaseObjectPtr obj;

	{
		SW_IGNORE_CHANGES_AUTOGUARD;

		obj = (CBaseObject*)rtClass->CreateObject();
		obj->SetClassDesc( cls );
		obj->SetLayer( m_pLayerManager->GetCurrentLayer() );
		obj->InitVariables();
		CoCreateGuid( &obj->m_guid ); // generate uniq GUID for this object.

		GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( obj );
		if (obj->Init( GetIEditor(),prev,file ))
		{
			if (obj->GetName().IsEmpty())
			{
				obj->SetName( GenUniqObjectName( cls->ClassName() ) );;
			}
			/*
			// Check if object Init function changed its name.
			if (objName.Compare(obj->GetName()) != 0)
			{
			// Object changed name. we must make sure it unique and generate new id.
			obj->SetName( GenUniqObjectName(obj->GetName()) );
			// Generate new unique id for this object.
			int id = Crc32Gen::GetCRC32(obj->GetName());
			obj->SetId(id);	
			}
			*/

			// Create game object itself.
			obj->CreateGameObject();

			if (!AddObject( obj ))
				obj = 0;
		}
		else
		{
			obj = 0;
		}
		GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL );
	}

	GetIEditor()->ResumeUndo();
	
	if (obj != 0 && GetIEditor()->IsUndoRecording())
	{
		GetIEditor()->GetConsoleSync()->OnObjectModified(obj,false,false);
		GetIEditor()->RecordUndo( new CUndoBaseObjectNew(obj) );
	}

	return obj;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::NewObject( CObjectArchive &ar,CBaseObject *pUndoObject,bool bMakeNewId )
{
	XmlNodeRef objNode = ar.node;

	// Load all objects from XML.
	CString typeName;
	GUID id = GUID_NULL;
	if (!objNode->getAttr( "Type",typeName ))
		return 0;

	if (!objNode->getAttr( "Id",id ))
	{
		// Make new ID for object that doesn't have if.
		CoCreateGuid( &id );
	}

	if (bMakeNewId)
	{
		// Make new guid for this object.
		GUID newId;
		CoCreateGuid( &newId );
		ar.RemapID( id,newId ); // Mark this id remapped.
		id = newId;
	}

	CBaseObjectPtr pObject;
	if (pUndoObject)
	{
		// if undoing restore object pointer.
		pObject = pUndoObject;
	}
	else
	{
		// New object creation.

		// Suspend undo operations when initializing object.
		CUndoSuspend undoSuspender;

		CString entityClass;
		if (objNode->getAttr("EntityClass", entityClass))
			typeName = typeName + "::" + entityClass;

		CObjectClassDesc *cls = FindClass( typeName );
		if (!cls)
		{
			CLogFile::FormatLine( "Error: RuntimeClass %s not registered",(const char*)typeName );
			return 0;
		}

		CRuntimeClass *rtClass = cls->GetRuntimeClass();
		assert( rtClass->IsDerivedFrom(RUNTIME_CLASS(CBaseObject)) );

		pObject = (CBaseObject*)rtClass->CreateObject();
		pObject->SetClassDesc( cls );
		pObject->m_guid = id;
		pObject->SetLayer( m_pLayerManager->GetCurrentLayer() );
		pObject->InitVariables();

		CString objName;
		objNode->getAttr( "Name",objName );
		pObject->m_name = objName;

		// @FIXME: Make sure this id not taken.
		bool bSWFindWasEnabled = CSWManager::Get().GetDoc().IsSWFindObjectEnabled();
		SW_ENABLE_FIND_OBJ(false);

		CBaseObject *obj = FindObject(pObject->GetId());
		
		SW_ENABLE_FIND_OBJ(bSWFindWasEnabled);

		if (obj)
		{
			CString layerName;
			if(obj->GetLayer())
				layerName = " [" + obj->GetLayer()->GetName() + "]";
			
			// If id is taken.
			CString error;
			error.Format( _T("[Error] Object %s already exists in the Object Manager and has been deleted as it is a duplicate of object %s in layer %s."),(const char*)objName, (const char*)obj->GetName(),(const char*)layerName);
			CLogFile::WriteLine( error );

			if (!GetIEditor()->IsInTestMode() && !GetIEditor()->IsInLevelLoadTestMode())
			{
				CErrorRecord errorRecord;
				errorRecord.pObject=obj;
				errorRecord.count=1;
				errorRecord.severity = CErrorRecord::ESEVERITY_ERROR;
				errorRecord.error=error;
				errorRecord.description="Possible duplicate objects being loaded, potential fix is to remove duplicate objects from level files.";
				GetIEditor()->GetErrorReport()->ReportError(errorRecord);
			}

			return 0;
			//CoCreateGuid( &pObject->m_guid ); // generate uniq GUID for this object.
		}
	}

	GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( pObject );
	if (!pObject->Init( GetIEditor(),0,"" ))
	{
		GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL );
		return 0;
	}

	if (!AddObject( pObject ))
	{
		GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL );
		return 0;
	}

	//pObject->Serialize( ar );

	GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL );

	if (!pObject->GetLayer() && !GetIEditor()->Get3DEngine()->IsSegmentedWorldActive())
	{
		// Cannot be.
		assert(0);
	}

	if (pObject != 0 && pUndoObject == 0)
	{
		// If new object with no undo, record it.
		if (CUndo::IsRecording())
			GetIEditor()->RecordUndo( new CUndoBaseObjectNew(pObject) );
	}

	m_loadedObjects++;
	if (m_pLoadProgress && m_totalObjectsToLoad > 0)
		m_pLoadProgress->Step( (m_loadedObjects*100)/m_totalObjectsToLoad );

	return pObject;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::NewObject( const CString &typeName,CBaseObject *prev,const CString &file )
{ 
	// [9/22/2009 evgeny] If it is "Entity", figure out if a CEntity subclass is actually needed
	CString fullName = typeName + "::" + file;
	CObjectClassDesc* cls = FindClass(fullName);
	if (!cls)
	{
		cls = FindClass(typeName);
	}
	
	if (!cls)
	{
		GetIEditor()->GetSystem()->GetILog()->Log( "Warning: RuntimeClass %s (as well as %s) not registered",(const char*)typeName,(const char*)fullName );
		return 0;
	}	
	CBaseObject *pObject = NewObject( cls,prev,file );
	return pObject;
}
	
//////////////////////////////////////////////////////////////////////////
void	CObjectManager::DeleteObject( CBaseObject *obj )
{
	if (m_currEditObject == obj)
		EndEditParams();

	if (!obj)
		return;

	// If object already deleted.
	if (obj->CheckFlags(OBJFLAG_DELETED))
		return;

	NotifyObjectListeners( obj, CBaseObject::ON_PREDELETE );

	// Check if object is a group then delete all childs.
	if (obj->IsKindOf(RUNTIME_CLASS(CGroup)))
	{
		((CGroup*)obj)->DeleteAllChilds();
	}
	else if (obj->IsKindOf(RUNTIME_CLASS(CAITerritoryObject)))
	{
		FindAndRenameProperty2("aiterritory_Territory", obj->GetName(), "<None>");
	}
	else if (obj->IsKindOf(RUNTIME_CLASS(CAIWaveObject)))
	{
		FindAndRenameProperty2("aiwave_Wave", obj->GetName(), "<None>");
	}

	// This will detach all childs and store Undo for each detachment (So they can be restored with Undo)
	//obj->DetachAll();

	// Must be after object DetachAll to support restoring Parent/Child relations.
	if (CUndo::IsRecording())
	{
		// Store undo for all child objects.
		for (int i = 0; i < obj->GetChildCount(); i++)
		{
			if (!obj->GetChild(i)->CheckFlags(OBJFLAG_PREFAB))
				obj->GetChild(i)->StoreUndo("DeleteParent");
		}
		CUndo::Record( new CUndoBaseObjectDelete(obj) );
	}

	OnObjectModified( obj,true,false );

	GetIEditor()->SuspendUndo();

	// Release game resources.
	obj->Done();

	if(gEnv->pAISystem->GetNavigationSystem())
	{
		AABB objAAB;
		obj->GetBoundBox(objAAB);
		gEnv->pAISystem->GetNavigationSystem()->WorldChanged(objAAB);
	}

	NotifyObjectListeners( obj,CBaseObject::ON_DELETE );

	RemoveObject( obj );
	GetIEditor()->ResumeUndo();
	
	RefreshEntitiesAssignedToSelectedTnW();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::DeleteAllObjects()
{
	EndEditParams();

	ClearSelection();
	int i;

	InvalidateVisibleList();

	// Delete all selection groups.
	std::vector<CSelectionGroup*> sel;
	stl::map_to_vector( m_selections,sel );
	for (i = 0; i < sel.size(); i++)
	{
		delete sel[i];
	}
	m_selections.clear();

	std::vector<CBaseObjectPtr> objectsHolder;
	GetAllObjects( objectsHolder );
	for (i = 0; i < objectsHolder.size(); i++)
	{
		objectsHolder[i]->Done();
	}
	// Clear map.
	m_objects.clear();
	//! Delete object instances.
	objectsHolder.clear();

	// Clear name map.
	m_nameNumbersMap.clear();

	RefreshEntitiesAssignedToSelectedTnW();
}

CBaseObject* CObjectManager::CloneObject( CBaseObject *obj )
{
	ASSERT( obj );
	//CRuntimeClass *cls = obj->GetRuntimeClass();
	//CBaseObject *clone = (CBaseObject*)cls->CreateObject();
	//clone->CloneCopy( obj );
	CBaseObject *clone = NewObject( obj->GetClassDesc(),obj );
	return clone;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::FindObject( REFGUID guid ) const
{
	CBaseObject* result = stl::find_in_map( m_objects,guid,(CBaseObject*)0 );
#ifdef SEG_WORLD
	if (result)
		return result;

	result = CSWManager::Get().GetDoc().CreateBaseObjectFromMetaData(guid);
	if(!result)
	{
		//assert(0);
		//m_objects[guid] = NULL;
	}
#endif
	return result;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::FindObject( const CString &sName ) const
{
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *pObj = it->second;
		if (stricmp(pObj->GetName(), sName)==0)
			return pObj;
	}
	return NULL;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::FindObjectsOfType(const CRuntimeClass* pClass, std::vector<CBaseObject*>& result)
{
	result.clear();

	CBaseObjectsArray objects;
	GetObjects(objects);

	for (size_t i = 0, n = objects.size(); i < n; ++i)
	{
		CBaseObject* pObject = objects[i];
		if (pObject->IsKindOf(pClass))
		{
			result.push_back(pObject);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::FindObjectsInAABB(const AABB& aabb, std::vector<CBaseObject*>& result) const
{
	result.clear();

	CBaseObjectsArray objects;
	GetObjects(objects);

	for (size_t i = 0, n = objects.size(); i < n; ++i)
	{
		CBaseObject* pObject = objects[i];
		AABB aabbObj;
		pObject->GetBoundBox(aabbObj);
		if (aabb.IsIntersectBox(aabbObj))
		{
			result.push_back(pObject);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::AddObject( CBaseObject *obj )
{
	CBaseObjectPtr p = stl::find_in_map( m_objects,obj->GetId(),0 );
	if (p)
	{
		CErrorRecord err;
		err.error.Format( "New Object %s has Duplicate GUID %s, New Object Ignored",(const char*)obj->GetName(),GuidUtil::ToString(obj->GetId()) );
		err.severity = CErrorRecord::ESEVERITY_ERROR;
		err.pObject = obj;
		err.flags = CErrorRecord::FLAG_OBJECTID;
		GetIEditor()->GetErrorReport()->ReportError(err);

		return false;
	}
	m_objects[obj->GetId()] = obj;
  RegisterObjectName(obj->GetName());
	InvalidateVisibleList();
	NotifyObjectListeners( obj,CBaseObject::ON_ADD );
	return true;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RemoveObject( CBaseObject *obj )
{
	assert( obj != 0 );
	
  UpdateRegisterObjectName(obj->GetName());
  InvalidateVisibleList();

	m_objects.erase(obj->GetId());
	
	// Remove this object from selection groups.
	m_currSelection->RemoveObject(obj);
	std::vector<CSelectionGroup*> sel;
	stl::map_to_vector( m_selections,sel );
	for (int i = 0; i < sel.size(); i++)
	{
		sel[i]->RemoveObject(obj);
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GetAllObjects( std::vector<CBaseObjectPtr> &objects ) const
{
	objects.clear();
	objects.reserve( m_objects.size() );
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		objects.push_back( it->second );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::ChangeObjectId( CBaseObject *obj,int newId )
{
	/*
	assert( obj );
	CBaseObjectPtr pObject = obj;
	CBaseObjectPtr p;
	if (m_objects.Find(obj->GetId(),p))
	{
		// If object is already added to object list, change it in the map.
		m_objects.Erase( obj->GetId() );
		m_objects[newId] = pObject;
	}
	pObject->SetId( newId );
	*/
}

bool CObjectManager::IsDuplicateObjectNameWarning( CBaseObject *obj,const CString &newName,bool bShowMsgBox )
{
	CBaseObject *pExisting = FindObject( newName );
	if (pExisting)
	{
		CString sRenameWarning("");
		sRenameWarning.Format
			(
			"%s \"%s\" was NOT renamed to \"%s\" because %s with the same name already exists.",
			obj->GetClassDesc()->ClassName(),
			obj->GetName().GetString(),
			newName.GetString(),
			pExisting->GetClassDesc()->ClassName()
			);

		// If id is taken.
		CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING,sRenameWarning);

		if (bShowMsgBox)
		{
			 AfxMessageBox(sRenameWarning);
		}

		return true;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::ChangeObjectName( CBaseObject *obj,const CString &newName )
{
	assert( obj );

	if (newName != obj->GetName())
	{
		if (IsDuplicateObjectNameWarning(obj,newName))
			return;

		obj->SetName(newName);
		/*
		// Change ID of object together with its name.
		int id = Crc32Gen::GetCRC32(obj->GetName());
		ChangeObjectId( obj,id );
		*/
		NotifyObjectListeners( obj,CBaseObject::ON_RENAME );

		// Make sure object name edit field is updated in the object properties panel.
		obj->UpdateEditParams();
	}
}

//////////////////////////////////////////////////////////////////////////
int CObjectManager::GetObjectCount() const
{
	return m_objects.size();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GetObjects( CBaseObjectsArray &objects,CObjectLayer* layer )  const
{
	objects.clear();
	objects.reserve( m_objects.size() );
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		if (layer == 0 || it->second->GetLayer() == layer)
			objects.push_back( it->second );
	}
}

void CObjectManager::GetObjects( CBaseObjectsArray &objects,BaseObjectFilterFunctor const& filter ) const
{
	objects.clear();
	objects.reserve( m_objects.size() );
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		assert(it->second);
		if (filter.first(*it->second,filter.second))
			objects.push_back( it->second );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GetCameras( std::vector<CCameraObject*> &objects )
{
	objects.clear();
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *object = it->second;
		if (object->IsKindOf(RUNTIME_CLASS(CCameraObject)))
		{
			// Only consider camera sources.
			if (object->IsLookAtTarget())
				continue;
			objects.push_back( (CCameraObject*)object );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::SendEvent( ObjectEvent event )
{
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		if (obj->GetGroup())
			continue;
		obj->OnEvent( event );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::SendEvent( ObjectEvent event,const AABB &bounds )
{
	AABB box;
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		if (obj->GetGroup())
			continue;
		obj->GetBoundBox(box);
		if (bounds.IsIntersectBox(box))
			obj->OnEvent( event );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::Update()
{
	HWND hFocusWnd = GetFocus();

	bool bObjectMode = (GetIEditor()->GetEditTool() && GetIEditor()->GetEditTool()->IsKindOf(RUNTIME_CLASS(CObjectMode)));

	if (m_currSelection->GetCount() == 0)
	{
		// Nothing selected.
		EndEditParams();
		if (bObjectMode)
			GetIEditor()->ShowTransformManipulator(false);
	}
	else if (m_currSelection->GetCount() == 1)
	{
		if (!m_bSingleSelection)
			EndEditParams();

		CBaseObject *newSelObject = m_currSelection->GetObject(0);
		// Single object selected.
		if (m_currEditObject != m_currSelection->GetObject(0))
		{
			m_bSelectionChanged = false;
			if (!m_currEditObject || (m_currEditObject->GetRuntimeClass() != newSelObject->GetRuntimeClass()))
			{
				// If old object and new objects are of different classes.
				EndEditParams();
			}
			if (bObjectMode)
				BeginEditParams( newSelObject,OBJECT_EDIT );

			//AfxGetMainWnd()->SetFocus();
		}
	}
	else if (m_currSelection->GetCount() > 1)
	{
		// Multiple objects are selected.
		if (m_bSelectionChanged && bObjectMode)
		{
			m_bSelectionChanged = false;
			m_nLastSelCount=m_currSelection->GetCount();
			EndEditParams();
			bool bAllSameType = m_currSelection->SameObjectType();

			m_currEditObject = m_currSelection->GetObject(0);
			m_currEditObject->BeginEditMultiSelParams( bAllSameType );
		}
	}

	// Restore focus if it changed.
	if (hFocusWnd != GetFocus() && hFocusWnd)
		SetFocus( hFocusWnd );

	m_pPhysicsManager->Update();

	UpdateBoneAttachedEntities();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::HideObject( CBaseObject *obj,bool hide )
{
	assert( obj != 0 );
	// Remove object from main object set and put it to hidden set.
	obj->SetHidden( hide );
	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UnhideAll()
{
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		obj->SetHidden(false);
	}
	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::FreezeObject( CBaseObject *obj,bool freeze )
{
	assert( obj != 0 );
	// Remove object from main object set and put it to hidden set.
	obj->SetFrozen( freeze );
	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UnfreezeAll()
{
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		obj->SetFrozen(false);
	}
	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::SelectObject( CBaseObject *obj, bool bUseMask)
{
	assert( obj );

	// Check if can be selected.
	if (bUseMask && (!(obj->GetType() & gSettings.objectSelectMask)))
		return false;

	if (!obj->IsSelectable())
		return false;

	if (m_selectCallback)
	{
		if (!m_selectCallback->OnSelectObject( obj ))
			return true;
	}

	/*
	if (GetIEditor()->IsUndoRecording() && !obj->IsSelected())
	{
		GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) );
	}
	*/

	m_currSelection->AddObject( obj );
	SetObjectSelected( obj,true );

	GetIEditor()->Notify(eNotify_OnSelectionChange);

	return true;
}

void CObjectManager::SelectEntities(std::set<CEntityObject*>& s)
{
	for (std::set<CEntityObject*>::iterator it = s.begin(), end = s.end(); it != end; ++it)
	{
		SelectObject(*it);
	}
}

void CObjectManager::UnselectObject( CBaseObject *obj )
{
	/*
	if (GetIEditor()->IsUndoRecording() && obj->IsSelected())
	{
		GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) );
	}
	*/
	SetObjectSelected( obj,false );
	m_currSelection->RemoveObject( obj );
}

CSelectionGroup* CObjectManager::GetSelection( const CString &name ) const
{
	CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 );
	return selection;
}

void CObjectManager::GetNameSelectionStrings(std::vector<CString> &names)
{
	for (TNameSelectionMap::iterator it = m_selections.begin(); it!=m_selections.end(); ++it )
		names.push_back(it->first);
}

void CObjectManager::NameSelection( const CString &name )
{
	if (m_currSelection->IsEmpty())
		return;

	CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 );
	if (selection)
	{
		ASSERT( selection != 0 );
		// Check if trying to rename itself to the same name.
		if (selection == m_currSelection)
			return;
		m_selections.erase( name );
		delete selection;
	}
	selection = new CSelectionGroup;
	selection->Copy( *m_currSelection );
	selection->SetName( name );
	m_selections[name] = selection;
	m_currSelection = selection;
	m_defaultSelection.RemoveAll();

#ifdef SEG_WORLD
	for (int i = 0; i < m_currSelection->GetCount(); i++)
	{
		CBaseObject *obj = m_currSelection->GetObject(i);
		SW_ON_OBJ_EXTERNAL(obj);
	}
#endif
}

void CObjectManager::SWGetSelectionsObjectsGIUDs(std::vector<GUID> &guids)
{
	CSelectionGroup *objSelection = 0;

	for (TNameSelectionMap::iterator it = m_selections.begin(); it!=m_selections.end(); ++it)
	{
		objSelection = it->second;

		if (!objSelection) 
			continue;

		if (objSelection->GetCount()==0) 
			continue;

		for (int i=0;i<objSelection->GetCount();++i)
		{
			if (objSelection->GetObject(i))
			{
				guids.push_back(objSelection->GetObject(i)->GetId());				
			}
		}
	}
}

void CObjectManager::SerializeNameSelection( XmlNodeRef &rootNode, bool bLoading)
{
	if (!rootNode)
		return;

	_smart_ptr<CSelectionGroup> tmpGroup(0);

	CString selRootStr("NameSelection");
	CString selNodeStr("NameSelectionNode");
	CString selNodeNameStr("name");
	CString idStr("id");
	CString objAttrStr("obj");

	XmlNodeRef startNode = rootNode->findChild( selRootStr );

	if (bLoading)
	{
		m_selections.erase(m_selections.begin(),m_selections.end());

		if (startNode)
		{
			for ( int selNodeNo=0; selNodeNo<startNode->getChildCount(); ++selNodeNo )
			{
				XmlNodeRef selNode = startNode->getChild(selNodeNo);
				tmpGroup = new CSelectionGroup;

				for (int objIDNodeNo=0 ; objIDNodeNo<selNode->getChildCount(); ++objIDNodeNo )
				{
					GUID curID= GUID_NULL;
					XmlNodeRef idNode= selNode->getChild(objIDNodeNo);
					if (!idNode->getAttr(idStr,curID))
						continue;

					if (curID!=GUID_NULL)
						if (GetIEditor()->GetObjectManager()->FindObject(curID))
							tmpGroup->AddObject(GetIEditor()->GetObjectManager()->FindObject(curID));
				}

				if (tmpGroup->GetCount()>0)
				{
					CString nameStr("");
					if (!selNode->getAttr(selNodeNameStr,nameStr))
						continue;

					tmpGroup->SetName(nameStr);
					m_selections[nameStr]=tmpGroup;
				}
			}
		}
	}
	else
	{
		startNode = rootNode->newChild( selRootStr );
		CSelectionGroup *objSelection = 0;

		for (TNameSelectionMap::iterator it = m_selections.begin(); it!=m_selections.end(); ++it)
		{
			XmlNodeRef selectionNameNode = startNode->newChild( selNodeStr );				
			selectionNameNode->setAttr(selNodeNameStr,it->first);
			objSelection = it->second;

			if (!objSelection) 
				continue;

			if (objSelection->GetCount()==0) 
				continue;

			for (int i=0;i<objSelection->GetCount();++i)
			{
				if (objSelection->GetObject(i))
				{
					XmlNodeRef objNode =selectionNameNode->newChild( objAttrStr );				
					objNode->setAttr(idStr,GuidUtil::ToString( objSelection->GetObject(i)->GetId()));
					SW_ON_OBJ_EXTERNAL(objSelection->GetObject(i));
				}
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
int CObjectManager::ClearSelection()
{
	int numSel = m_currSelection->GetCount();
	UnselectCurrent();
	m_defaultSelection.RemoveAll();
	m_currSelection = &m_defaultSelection;
	m_bSelectionChanged = true;

	if (!m_bExiting)
		GetIEditor()->Notify(eNotify_OnSelectionChange);

	return numSel;
}

//////////////////////////////////////////////////////////////////////////
int CObjectManager::InvertSelection()
{
	int selCount = 0;
	// iterate all objects.
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *pObj = it->second;
		if (pObj->IsSelected())
			UnselectObject( pObj );
		else
		{
			if (SelectObject( pObj ))
				selCount++;
		}
	}
	return selCount;
}

void CObjectManager::SetSelection( const CString &name )
{
	CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 );
	if (selection)
	{
		UnselectCurrent();
		ASSERT( selection != 0 );
		m_currSelection = selection;
		SelectCurrent();
	}
}

void CObjectManager::RemoveSelection( const CString &name )
{
	CString selName = name;
	CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 );
	if (selection)
	{
		if (selection == m_currSelection)
		{
			UnselectCurrent();
			m_currSelection = &m_defaultSelection;
			m_defaultSelection.RemoveAll();
		}
		delete selection;
		m_selections.erase( selName );
	}
}

void CObjectManager::SelectCurrent()
{
	for (int i = 0; i < m_currSelection->GetCount(); i++)
	{
		CBaseObject *obj = m_currSelection->GetObject(i);
		if (GetIEditor()->IsUndoRecording() && !obj->IsSelected())
			GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) );

		SetObjectSelected( obj,true );
	}
}

void CObjectManager::UnselectCurrent()
{
	// Make sure to unlock selection.
	GetIEditor()->LockSelection( false );

	for (int i = 0; i < m_currSelection->GetCount(); i++)
	{
		CBaseObject *obj = m_currSelection->GetObject(i);
		if (GetIEditor()->IsUndoRecording() && obj->IsSelected())
			GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) );

		SetObjectSelected( obj,false );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::Display( DisplayContext &dc )
{
	Vec3 org;

	FUNCTION_PROFILER( GetIEditor()->GetSystem(),PROFILE_EDITOR );

	if (!m_bVisibleObjectValid || gSettings.objectHideMask != m_lastHideMask)
	{
		m_lastHideMask = gSettings.objectHideMask;
		UpdateVisibilityList();
	}

	if (dc.settings->IsDisplayHelpers() || 	dc.view->GetVisibleObjectsCache()->GetObjectCount()==0)		// is display helpers or list is not populated (needed to select objects)
	{
		FindDisplayableObjects(dc, true);
	}

	if (m_gizmoManager)
	{
		m_gizmoManager->Display( dc );
	}
}

void CObjectManager::ForceUpdateVisibleObjectCache(DisplayContext& dc)
{
	FindDisplayableObjects(dc, false);
}

void CObjectManager::FindDisplayableObjects(DisplayContext& dc, bool bDisplay)
{
	CBaseObjectsCache *pDispayedViewObjects = dc.view->GetVisibleObjectsCache();

	const CCamera &camera = GetIEditor()->GetSystem()->GetViewCamera();
	AABB bbox;
	bbox.min.zero();
	bbox.max.zero();

	pDispayedViewObjects->ClearObjects();
	pDispayedViewObjects->Reserve(m_visibleObjects.size());

	if (dc.flags & DISPLAY_2D)
	{
		int numVis = m_visibleObjects.size();
		for (int i = 0; i < numVis; i++)
		{
			CBaseObject *obj = m_visibleObjects[i];

			obj->GetBoundBox( bbox );
			if (dc.box.IsIntersectBox( bbox ))
			{
				pDispayedViewObjects->AddObject(obj);

				if (bDisplay && dc.settings->IsDisplayHelpers() && (gSettings.viewports.nShowFrozenHelpers || !obj->IsFrozen()))
					obj->Display( dc );
			}
		}
	}
	else
	{
		int numVis = m_visibleObjects.size();
		for (int i = 0; i < numVis; i++)
		{
			CBaseObject *obj = m_visibleObjects[i];

			obj->GetBoundBox( bbox );
			if (obj && obj->IsInCameraView(camera))
			{
				// Check if object is too far.
				float visRatio = obj->GetCameraVisRatio(camera);
				if (visRatio > m_maxObjectViewDistRatio || (dc.flags&DISPLAY_SELECTION_HELPERS) || obj->IsSelected())
				{
					pDispayedViewObjects->AddObject(obj);

					if (bDisplay && dc.settings->IsDisplayHelpers() && (gSettings.viewports.nShowFrozenHelpers || !obj->IsFrozen()))
						obj->Display( dc );
				}
			}
		}
	}
}

void CObjectManager::BeginEditParams( CBaseObject *obj,int flags )
{
	ASSERT( obj != 0 );
	if (obj == m_currEditObject)
		return;

	if (GetSelection()->GetCount() > 1)
		return;

	HWND hFocusWnd = GetFocus();
	
	if (m_currEditObject)
	{
		//if (obj->GetClassDesc() != m_currEditObject->GetClassDesc())
		if (!obj->IsSameClass(m_currEditObject))
			EndEditParams( flags );
	}

	m_currEditObject = obj;

	if (flags & OBJECT_CREATE)
	{
		// Unselect all other objects.
		ClearSelection();
		// Select this object.
		SelectObject( obj, false );
	}
	
	m_bSingleSelection = true;
	m_currEditObject->BeginEditParams( GetIEditor(),flags );

	// Restore focus if it changed.
	if (hFocusWnd != GetFocus() && hFocusWnd)
		SetFocus( hFocusWnd );
}
	
void CObjectManager::EndEditParams( int flags )
{
	if (m_currEditObject)
	{
		if (m_bSingleSelection)
			m_currEditObject->EndEditParams( GetIEditor() );
		else
			m_currEditObject->EndEditMultiSelParams();
	}
	m_bSingleSelection = false;
	m_currEditObject = 0;
	//m_bSelectionChanged = false; // don't need to clear for ungroup
}

//! Select objects within specified distance from given position.
int CObjectManager::SelectObjects( const AABB &box,bool bUnselect)
{
	int numSel = 0;

	AABB objBounds;
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;

		if (obj->IsHidden())
			continue;
		if (obj->IsFrozen())
			continue;

		if (obj->GetGroup())
			continue;

		obj->GetBoundBox( objBounds );
		if (box.IsIntersectBox(objBounds))
		{
			numSel++;
			if (!bUnselect)
				SelectObject( obj );
			else
				UnselectObject( obj );
		}
		// If its group.
		if (obj->GetRuntimeClass() == RUNTIME_CLASS(CGroup))
		{
			numSel += ((CGroup*)obj)->SelectObjects( box,bUnselect );
		}
	}
	return numSel;
}

//////////////////////////////////////////////////////////////////////////
int CObjectManager::MoveObjects( const AABB &box, const Vec3 & offset, int nRot, bool bIsCopy )
{
	AABB objBounds;

	Vec3 src = (box.min+box.max)/2;
	Vec3 dst = src+offset;
	float alpha = 3.141592653589793f/2 * nRot;
	float cosa=cos(alpha);
	float sina=sin(alpha);

	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;

		if(obj->GetParent())
			continue;

		if (obj->GetGroup())
			continue;

		obj->GetBoundBox( objBounds );
		if (box.IsIntersectBox(objBounds))
		{
			if(nRot==0)
			{
				obj->SetPos(obj->GetPos()-src+dst);
			}
			else
			{
				Vec3 pos = obj->GetPos()-src;
				Vec3 newPos(pos);
				newPos.x = cosa * pos.x - sina * pos.y;
				newPos.y = sina * pos.x + cosa * pos.y;
				obj->SetPos(newPos + dst);
				Quat q;
				obj->SetRotation(q.CreateRotationZ(alpha) * obj->GetRotation());
			}
		}
	}
	return 0;
}

bool CObjectManager::IsObjectDeletionAllowed(CBaseObject *pObject)
{
	if (!pObject)
		return false;

	// Test AI object against AI/Physics activation
	/*uint32 flags = GetIEditor()->GetDisplaySettings()->GetSettings();
	if ((flags&SETTINGS_PHYSICS) != 0)
	{
		if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			CEntityObject *pEntityObj = (CEntityObject*)pObject;

			if (pEntityObj)
			{
				IEntity *pIEntity = pEntityObj->GetIEntity();
				if (pIEntity)
				{
					if (pIEntity->HasAI())
					{
						CString msg("");
						msg.Format( _T("AI object %s cannot be deleted when AI/Physics mode is activated."), (const char*)pObject->GetName() );
						AfxMessageBox(msg);
						return false;
					}
				}							
			}
		}
	}*/

	return true;
};

//////////////////////////////////////////////////////////////////////////
void CObjectManager::DeleteSelection()
{
	// Make sure to unlock selection.
	GetIEditor()->LockSelection( false );

	GUID bID=GUID_NULL;

	int i;
	std::vector<CBaseObjectPtr> objects;
	for (i = 0; i < m_currSelection->GetCount(); i++)
	{
		if (bID==GUID_NULL)
			if (CProceduralCreationWorks::IsProceduralObjectSelected(m_currSelection->GetObject(i)))
			{
				if (m_currSelection->GetObject(i)->GetParent())
					bID=m_currSelection->GetObject(i)->GetParent()->GetId();
			}
			else
				if (CProceduralCreationWorks::IsProceduralBuildingInSelection(m_currSelection))
				{
					if (m_currSelection->GetObject(i)->IsKindOf(RUNTIME_CLASS(CPrefabBuildingObject)))
						bID=m_currSelection->GetObject(i)->GetId();
				};
		
		// Check condition(s) if object could be deleted
		if ( !IsObjectDeletionAllowed( m_currSelection->GetObject(i)) )
		{
			return;
		}

		objects.push_back( m_currSelection->GetObject(i) );
	}

	RemoveSelection( m_currSelection->GetName() );
	m_currSelection = &m_defaultSelection;
	m_defaultSelection.RemoveAll();

	// When deleting group, subobject of group must become selected.
	std::vector<CBaseObjectPtr> toselect;

	for (i = 0; i < objects.size(); i++)
	{
		DeleteObject( objects[i] );
	}

	if (bID!=GUID_NULL)
		CProceduralCreationWorks::UpdateBuilding(bID);

	GetIEditor()->GetFlowGraphManager()->SendNotifyEvent( EHG_GRAPH_INVALIDATE );
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::HitTestObject(CBaseObject *obj, HitContext & hc)
{
	if (obj->IsFrozen())
		return false;

	if (obj->IsHidden())
		return false;

	// This object is rejected by deep selection.
	if(obj->CheckFlags(OBJFLAG_NO_HITTEST))
		return false;

	ObjectType objType = obj->GetType();

	// Check if this object type is masked for selection.
	if (!(objType & gSettings.objectSelectMask))
		return false;

	const bool bSelectionHelperHit = obj->HitHelperTest(hc);

	if (hc.bUseSelectionHelpers && !bSelectionHelperHit)
		return false;

	if (!bSelectionHelperHit)
	{
		// Fast checking.
		if (hc.camera && !obj->IsInCameraView(*hc.camera))
		{
			return false;
		}
		else if (hc.bounds && !obj->IntersectRectBounds(*hc.bounds))
		{
			return false;
		}
		
		// Do 2D space testing.
		if (hc.nSubObjFlags == 0)
		{
			Ray ray(hc.raySrc, hc.rayDir);
			if (!obj->IntersectRayBounds(ray))
				return false;
		}
		else if (!obj->HitTestRect(hc))
		{
			return false;
		}
	}

	return (bSelectionHelperHit || obj->HitTest(hc));
}


//////////////////////////////////////////////////////////////////////////
bool CObjectManager::HitTest( HitContext &hitInfo )
{
	FUNCTION_PROFILER( GetIEditor()->GetSystem(),PROFILE_EDITOR );

	hitInfo.object = 0;
	hitInfo.dist = 0;
	hitInfo.axis = 0;
	hitInfo.manipulatorMode = 0;

	HitContext hcOrg = hitInfo;
	if (hcOrg.view)
	{
		hcOrg.b2DViewport = hcOrg.view->GetType() != ET_ViewportCamera;
	}
	hcOrg.rayDir = hcOrg.rayDir.GetNormalized();

	HitContext hc = hcOrg;
		
	float mindist = FLT_MAX;

	if (!hitInfo.bIgnoreAxis && !hc.bUseSelectionHelpers)
	{
		// Test gizmos.
		if (m_gizmoManager->HitTest( hc ))
		{
			if (hc.axis != 0)
			{
				hitInfo.object = hc.object;
				hitInfo.gizmo = hc.gizmo;
				hitInfo.axis = hc.axis;
				hitInfo.manipulatorMode = hc.manipulatorMode;
				hitInfo.dist = hc.dist;
				return true;
			}
		}
	}

	if (hitInfo.bOnlyGizmo)
		return false;

	// Only HitTest objects, that where previously Displayed.
	CBaseObjectsCache *pDispayedViewObjects = hitInfo.view->GetVisibleObjectsCache();

	CBaseObject *selected = 0;
	int numVis = pDispayedViewObjects->GetObjectCount();
	for (int i = 0; i < numVis; i++)
	{
		CBaseObject *obj = pDispayedViewObjects->GetObject(i);

		//! Only check root objects.
		//! One exception: a child should be checked, if belonged group is opened.
		if (obj->GetGroup())
		{
			if(!obj->GetGroup()->IsOpen())
				continue;
		}

		const bool isGroup = (obj->GetType()==OBJTYPE_PREFAB || obj->GetType()==OBJTYPE_GROUP);
		const bool hitInGroup = isGroup && obj->HitTest(hc) && hc.object;
		
		if(hitInGroup)
			obj = hc.object;
		
		if(hitInGroup || HitTestObject(obj, hc))
		{
			if (m_selectCallback && !m_selectCallback->CanSelectObject(obj))
				continue;

			// Check if this object is nearest.
			if (hc.axis != 0)
			{
				hitInfo.object = obj;
				hitInfo.axis = hc.axis;
				hitInfo.dist = hc.dist;
				return true;
			}
			
			if (hc.dist < mindist)
			{
				// If collided object specified, accept it, otherwise take tested object itself.
				CBaseObject *hitObj = hc.object;
				if (!hitObj)
					hitObj = obj;

				hc.object = 0;

				mindist = hc.dist;
				selected = hitObj;
			}

			// If use deep selection
			if(hitInfo.pDeepSelection)
			{
				hitInfo.pDeepSelection->AddObject(hc.dist, obj);
			}
		}
	}

	if (selected)
	{
		hitInfo.object = selected;
		hitInfo.dist = mindist;
		return true;
	}
	return false;
}
void CObjectManager::FindObjectsInRect( CViewport *view,const CRect &rect, std::vector<GUID> &guids)
{
	if (rect.Width() < 1 || rect.Height() < 1)
		return;

	AABB box;
	HitContext hc;
	hc.view = view;
	hc.b2DViewport = view->GetType() != ET_ViewportCamera;
	hc.rect = rect;

	guids.clear();

	CBaseObjectsCache *pDispayedViewObjects = view->GetVisibleObjectsCache();

	int numVis = pDispayedViewObjects->GetObjectCount();
	for (int i = 0; i < numVis; ++i)
	{
		CBaseObject *pObj = pDispayedViewObjects->GetObject(i);

		if (!pObj->IsSelectable())
			continue;

		// Retrieve world space bound box.
		pObj->GetBoundBox( box );

		// Check if object visible in viewport.
		if (!view->IsBoundsVisible(box))
			continue;

		if (pObj->HitTestRect(hc))
			guids.push_back(pObj->GetId());
	}
}
//////////////////////////////////////////////////////////////////////////
void CObjectManager::SelectObjectsInRect( CViewport *view,const CRect &rect,bool bSelect )
{
	// Ignore too small rectangles.
	if (rect.Width() < 1 || rect.Height() < 1)
		return;

	CUndo undo( "Select Object(s)" );

	AABB box;

	HitContext hc;
	hc.view = view;
	hc.b2DViewport = view->GetType() != ET_ViewportCamera;
	hc.rect = rect;

	CBaseObjectsCache *pDispayedViewObjects = view->GetVisibleObjectsCache();
	int numVis = pDispayedViewObjects->GetObjectCount();
	for (int i = 0; i < numVis; ++i)
	{
		CBaseObject *pObj = pDispayedViewObjects->GetObject(i);
		
		if (!pObj->IsSelectable())
			continue;

		// Retrieve world space bound box.
		pObj->GetBoundBox( box );
		
		// Check if object visible in viewport.
		if (!view->IsBoundsVisible(box))
			continue;

		if (pObj->HitTestRect(hc))
		{
			if (bSelect)
				SelectObject(pObj);
			else
				UnselectObject(pObj);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RegisterObjectName( const CString &name )
{
	// Remove all numbers from the end of typename.
	CString typeName = name;
	int nameLen = typeName.GetLength();
	int len = nameLen;
	while (len > 0 && isdigit(typeName[len-1]))
		len--;

	typeName = typeName.Left(len);

	int num = 0;
	if (len < nameLen)
		num = atoi((const char*)name+len) + 0;

	int lastNumber = stl::find_in_map( m_nameNumbersMap,typeName,0 );
	int newLastNumber = MAX( num,lastNumber );
	if (newLastNumber != lastNumber)
		m_nameNumbersMap[typeName] = newLastNumber;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UpdateRegisterObjectName( const CString &name )
{
  // Remove all numbers from the end of typename.
  CString typeName = name;
  int nameLen = typeName.GetLength();
  int len = nameLen;

  while (len > 0 && isdigit(typeName[len-1]))
    len--;

  typeName = typeName.Left(len);

  int num = 0;
  if (len < nameLen)
    num = atoi((const char*)name+len) + 0;

  NameNumbersMap::iterator it = m_nameNumbersMap.find(typeName);

  if (it != m_nameNumbersMap.end() && num == it->second)
  {
    if (--num < 1)
      m_nameNumbersMap.erase(it);
    else
      m_nameNumbersMap[typeName]= num;
  }
}

//////////////////////////////////////////////////////////////////////////
CString	CObjectManager::GenUniqObjectName( const CString &theTypeName )
{
	if (!m_bGenUniqObjectNames)
		return theTypeName;

	CString typeName = theTypeName;
	if (const char *subName = ::strstr(theTypeName, "::"))
	{
		size_t length = ::strlen(subName);
		if (length > 2)
			typeName = subName + 2;
	}

	// Remove all numbers from the end of typename.
	int len = typeName.GetLength();
	while (len > 0 && isdigit(typeName[len-1]))
		len--;
	
	if (len > 0 && typeName[len-1] == '_')
		len--;
	
	typeName = typeName.Left(len);

	int lastNumber = stl::find_in_map( m_nameNumbersMap,typeName,0 );
	lastNumber++;
	m_nameNumbersMap[typeName] = lastNumber;

	CString str;
	str.Format( "%s_%02d",(const char*)typeName,lastNumber );

	/*
	CString tpName = typeName;
	char str[1024];
	int num = 0;
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		const char *name = it->second->GetName();
		if (strncmp(name,tpName,len) == 0)
		{
			int n = atoi(name+len) + 1;
			num = MAX( num,n );
		}
	}
	//sprintf( str,"%s%02d",(const char*)typeName,num );
	sprintf( str,"%s%d",(const char*)typeName,num );
	*/
	return str;
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::EnableUniqObjectNames( bool bEnable )
{
	bool bPrev = m_bGenUniqObjectNames;
	m_bGenUniqObjectNames = bEnable;
	return bPrev;
}

//////////////////////////////////////////////////////////////////////////
CObjectClassDesc* CObjectManager::FindClass( const CString &className )
{
	IClassDesc *cls = CClassFactory::Instance()->FindClass( className );
	if (cls != NULL && cls->SystemClassID() == ESYSTEM_CLASS_OBJECT)
	{
		return (CObjectClassDesc*)cls;
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GetClassCategories( std::vector<CString> &categories )
{
	std::vector<IClassDesc*> classes;
	CClassFactory::Instance()->GetClassesBySystemID( ESYSTEM_CLASS_OBJECT,classes );
	std::set<CString> cset;
	for (int i = 0; i < classes.size(); i++)
	{
		const char *category = classes[i]->Category();
		if (strlen(category) > 0)
			cset.insert( category );
	}
	categories.clear();
	categories.reserve( cset.size() );
	for (std::set<CString>::iterator cit = cset.begin(); cit != cset.end(); ++cit)
	{
		categories.push_back( *cit );
	}
}
	
void CObjectManager::GetClassTypes( const CString &category,std::vector<CString> &types )
{
	std::vector<IClassDesc*> classes;
	CClassFactory::Instance()->GetClassesBySystemID( ESYSTEM_CLASS_OBJECT,classes );
	for (int i = 0; i < classes.size(); i++)
	{
		const char* cat = classes[i]->Category();
		if (stricmp(cat,category) == 0)
		{
			types.push_back( classes[i]->ClassName() );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RegisterClassTemplate( XmlNodeRef &templ )
{
	CString typeName = templ->getTag();
	CString superTypeName;
	if (!templ->getAttr( "SuperType",superTypeName ))
		return;

	CObjectClassDesc *superType = FindClass( superTypeName );
	if (!superType)
		return;

	CString category,fileSpec,initialName;
	templ->getAttr( "Category",category );
	templ->getAttr( "File",fileSpec );
	templ->getAttr( "Name",initialName );

	CXMLObjectClassDesc *classDesc = new CXMLObjectClassDesc;
	classDesc->superType = superType;
	classDesc->type = typeName;
	classDesc->category = category;
	classDesc->fileSpec = fileSpec;
	CoCreateGuid( &classDesc->guid );
	//classDesc->properties = templ->findChild( "Properties" );

	CClassFactory::Instance()->RegisterClass( classDesc );
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::LoadClassTemplates( const CString &path )
{
	CString dir = Path::AddBackslash(path);

	std::vector<CFileUtil::FileDesc> files;
	CFileUtil::ScanDirectory( dir,"*.xml",files,false );

	for (int k = 0; k < files.size(); k++)
	{

		// Construct the full filepath of the current file
		XmlNodeRef node = XmlHelpers::LoadXmlFromFile( dir + files[k].filename );
		if (node != 0 && node->isTag("ObjectTemplates"))
		{
			CString name;
			for (int i = 0; i < node->getChildCount(); i++)
			{
				RegisterClassTemplate( node->getChild(i) );
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::Serialize( XmlNodeRef &xmlNode,bool bLoading,int flags )
{
	if (!xmlNode)
		return;

	bool bIgnoreExternal = flags & SERIALIZE_IGNORE_EXTERNAL;

	if (bLoading)
	{
		m_loadedObjects = 0;

		if (flags == SERIALIZE_ONLY_NOTSHARED)
			DeleteNotSharedObjects();
		else if (flags == SERIALIZE_ONLY_SHARED)
			DeleteSharedObjects();
		else
			DeleteAllObjects();


		XmlNodeRef root = xmlNode->findChild( "Objects" );
		
		int totalObjects = 0;
		if (root)
			root->getAttr( "NumObjects",totalObjects );

		StartObjectsLoading( totalObjects );

		// Load layers.
		CObjectArchive ar( this,xmlNode,true );
	
		// Load layers.
		m_pLayerManager->Serialize( ar,bIgnoreExternal );

		// Loading.
		if (root)
		{
			ar.node = root;
			LoadObjects( ar,false );
		}
		EndObjectsLoading();
	}
	else
	{
		// Saving.
		XmlNodeRef root = xmlNode->newChild( "Objects" );

		CObjectArchive ar( this,root,false );

		int totalObjects = m_objects.size();
		root->setAttr( "NumObjects",totalObjects );

		// Save all objects to XML.
		for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
		{
			CBaseObject *obj = it->second;

			// Not save objects in prefabs or groups.
			if (obj->GetGroup() || obj->CheckFlags(OBJFLAG_PREFAB))
				continue;

			if ((flags == SERIALIZE_ONLY_SHARED) && !obj->CheckFlags(OBJFLAG_SHARED))
				continue;
			else if ((flags == SERIALIZE_ONLY_NOTSHARED) && obj->CheckFlags(OBJFLAG_SHARED))
				continue;

			CObjectLayer *pLayer = obj->GetLayer();
			if (pLayer->IsExternal() || pLayer->IsParentExternal())
				continue;
			
			XmlNodeRef objNode = root->newChild( "Object" );
			ar.node = objNode;
			obj->Serialize( ar );
		}

		// Save layers.
		ar.node = xmlNode;
		m_pLayerManager->Serialize( ar,bIgnoreExternal );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::LoadObjects( CObjectArchive &objectArchive,bool bSelect )
{
	m_bLoadingObjects = true;
	m_bSkipMessages = false;

	XmlNodeRef objectsNode = objectArchive.node;
	int numObjects = objectsNode->getChildCount();
	for (int i = 0; i < numObjects; i++)
	{
		objectArchive.node = objectsNode->getChild(i);
		CBaseObject *obj = objectArchive.LoadObject( objectsNode->getChild(i) );
		if (obj && bSelect)
			SelectObject( obj );
	}
	EndObjectsLoading(); // End progress bar, here, Resolve objects have his own.
	objectArchive.ResolveObjects();

	InvalidateVisibleList();

	m_bLoadingObjects = false;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::Export( const CString &levelPath,XmlNodeRef &rootNode,bool onlyShared )
{
	// Clear export files.
	DeleteFile( levelPath+"TagPoints.ini" );
	DeleteFile( levelPath+"Volumes.ini" );
	
	// Save all objects to XML.
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		CObjectLayer *pLayer = obj->GetLayer();
		if (!pLayer->IsExportable())
			continue;
		// Export Only shared objects.
		if ((obj->CheckFlags(OBJFLAG_SHARED) && onlyShared) ||
				(!obj->CheckFlags(OBJFLAG_SHARED) && !onlyShared))
		{
			obj->Export( levelPath,rootNode );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::ExportEntities( XmlNodeRef &rootNode )
{
	// Save all objects to XML.
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		CObjectLayer *pLayer = obj->GetLayer();
		if (!pLayer->IsExportable())
			continue;
		if (obj->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			obj->Export( "",rootNode );
		}
	}
}

void CObjectManager::DeleteNotSharedObjects()
{
	std::vector<CBaseObjectPtr> objects;
	GetAllObjects( objects );
	for (int i = 0; i < objects.size(); i++)
	{
		CBaseObject *obj = objects[i];
		if (!obj->CheckFlags(OBJFLAG_SHARED))
		{
			DeleteObject( obj );
		}
	}
}

void CObjectManager::DeleteSharedObjects()
{
	std::vector<CBaseObjectPtr> objects;
	GetAllObjects( objects );
	for (int i = 0; i < objects.size(); i++)
	{
		CBaseObject *obj = objects[i];
		if (obj->CheckFlags(OBJFLAG_SHARED))
		{
			DeleteObject( obj );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
IObjectSelectCallback* CObjectManager::SetSelectCallback( IObjectSelectCallback* callback )
{
	IObjectSelectCallback* prev = m_selectCallback;
	m_selectCallback = callback;
	return prev;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::InvalidateVisibleList()
{
	if(m_isUpdateVisibilityList)
		return;
	m_bVisibleObjectValid = false;
	m_visibleObjects.clear();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UpdateVisibilityList()
{
	m_isUpdateVisibilityList = true;
	m_visibleObjects.clear();
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		bool visible = obj->IsPotentiallyVisible();
		obj->UpdateVisibility( visible );
		if (visible)
		{
			// Prefabs are not added into visible list.
			if (!obj->CheckFlags(OBJFLAG_PREFAB))
				m_visibleObjects.push_back( obj );
		}
	}
	m_bVisibleObjectValid = true;
	m_isUpdateVisibilityList = false;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::FindAnimNodeOwner( IAnimNode* node ) const
{
	if (!node)
		return 0;
	IAnimNodeOwner *pOwner = node->GetNodeOwner();
	if (pOwner)
	{
		CEntityObject *pEntity = static_cast<CEntityObject*>(pOwner);
		return pEntity;
	}

	EntityGUID *pEntityGuid = node->GetEntityGuid();
	if (pEntityGuid == 0 || *pEntityGuid == 0)
	{
		return 0;
	}

	// Find owner entity.
	IEntity *pIEntity = node->GetEntity();
	if (pIEntity)
	{
		// Find owner editor entity.
		CEntityObject *pEntity = CEntityObject::FindFromEntityId( pIEntity->GetId() );
		return pEntity;
	}

	// Slow way, scan everything.
	int nodeType = node->GetType();
	if (nodeType == ANODE_ENTITY || nodeType == ANODE_CAMERA)
	{
		uint32 animNodeId = node->GetId();
		for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
		{
			CBaseObject *obj = it->second;
			if (obj && obj->m_guid.Data1 == animNodeId)
			{
				if (obj->IsKindOf(RUNTIME_CLASS(CEntityObject)))
					return obj;
			}
		}
	}

	return 0;
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::ConvertToType( CBaseObject *object,ObjectType objectType )
{
	if (objectType == OBJTYPE_BRUSH)
	{
		if (object->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			CUndo undo( "Convert To Brush" );
			// Can convert Entity to brush.
			CBaseObjectPtr brushObject = GetIEditor()->NewObject( "Brush" );
			if (!brushObject)
				return false;

			if (!brushObject->ConvertFromObject( object ))
			{
				// delete this object.
				DeleteObject( brushObject );
				return false;
			}
			DeleteObject( object );
			return true;
		}
	}
	else if (objectType == OBJTYPE_ENTITY)
	{
		if (object->IsKindOf(RUNTIME_CLASS(CBrushObject)) || object->IsKindOf(RUNTIME_CLASS(CEntityObject))  )
		{
			CUndo undo( "Convert To GeomEntity" );
			// Can convert Entity to brush.
			CBaseObjectPtr ent = GetIEditor()->NewObject( "GeomEntity" );
			if (!ent)
				return false;

			if (!ent->ConvertFromObject( object ))
			{
				// delete this object.
				DeleteObject( ent );
				return false;
			}
			DeleteObject( object );
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::SetObjectSelected( CBaseObject* pObject,bool bSelect )
{
	// Only select/unselect once.
	if ((pObject->IsSelected() && bSelect) || (!pObject->IsSelected() && !bSelect))
		return;

	// Store selection undo.
	if (CUndo::IsRecording())
		CUndo::Record( new CUndoBaseObjectSelect(pObject) );

	pObject->SetSelected(bSelect);
	m_bSelectionChanged = true;


	if (bSelect && !GetIEditor()->GetTransformManipulator())
	{
		if (CAxisGizmo::GetGlobalAxisGizmoCount() < gSettings.gizmo.axisGizmoMaxCount)
		{
			// Create axis gizmo for this object.
			m_gizmoManager->AddGizmo( new CAxisGizmo(pObject) );
		}
	}

	if (bSelect)
		NotifyObjectListeners( pObject,CBaseObject::ON_SELECT );
	else
		NotifyObjectListeners( pObject,CBaseObject::ON_UNSELECT );

	if (pObject->IsKindOf(RUNTIME_CLASS(CAITerritoryObject)) || pObject->IsKindOf(RUNTIME_CLASS(CAIWaveObject)))
	{
		RefreshEntitiesAssignedToSelectedTnW();
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::HideTransformManipulators()
{
	m_gizmoManager->DeleteAllTransformManipulators();
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CObjectManager::AddObjectEventListener( const EventCallback &cb )
{
	stl::push_back_unique( m_objectEventListeners,cb );
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RemoveObjectEventListener( const EventCallback &cb )
{
	stl::find_and_erase( m_objectEventListeners,cb );
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::NotifyObjectListeners( CBaseObject *pObject,CBaseObject::EObjectListenerEvent event )
{
	std::list<EventCallback>::iterator next;
	for (std::list<EventCallback>::iterator it = m_objectEventListeners.begin(); it != m_objectEventListeners.end(); it = next)
	{
		next = it;
		++next;
		// Call listener callback.
		(*it)( pObject,event );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::StartObjectsLoading( int numObjects )
{
	if (m_pLoadProgress)
		return;
	m_pLoadProgress = new CWaitProgress( _T("Loading Objects") );
	m_totalObjectsToLoad = numObjects;
	m_loadedObjects = 0;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::EndObjectsLoading()
{
	if (m_pLoadProgress)
		delete m_pLoadProgress;
	m_pLoadProgress = 0;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GatherUsedResources( CUsedResources &resources,CObjectLayer *pLayer )
{
	CBaseObjectsArray objects;
	GetIEditor()->GetObjectManager()->GetObjects( objects,pLayer );

	for (int i = 0; i < objects.size(); i++)
	{
		CBaseObject *pObject = objects[i];
		pObject->GatherUsedResources( resources );
	}
}

//////////////////////////////////////////////////////////////////////////
IGizmoManager* CObjectManager::GetGizmoManager()
{
	return m_gizmoManager;
}

//////////////////////////////////////////////////////////////////////////
IStatObj* CObjectManager::GetGeometryFromObject( CBaseObject *pObject )
{
	assert( pObject );

	if (pObject->IsKindOf(RUNTIME_CLASS(CBrushObject)))
	{
		CBrushObject *pBrushObj = (CBrushObject*)pObject;
		return pBrushObj->GetIStatObj();
	}
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntityObj = (CEntityObject*)pObject;
		if (pEntityObj->GetIEntity())
		{
			IEntity *pGameEntity = pEntityObj->GetIEntity();
			for (int i = 0; pGameEntity != NULL && i < pGameEntity->GetSlotCount(); i++)
			{
				if (pGameEntity->GetStatObj(i))
					return pGameEntity->GetStatObj(i);
			}
		}
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
ICharacterInstance* CObjectManager::GetCharacterFromObject( CBaseObject *pObject )
{
	assert( pObject );
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntityObj = (CEntityObject*)pObject;
		if (pEntityObj->GetIEntity())
		{
			IEntity *pGameEntity = pEntityObj->GetIEntity();
			for (int i = 0; pGameEntity != NULL && i < pGameEntity->GetSlotCount(); i++)
			{
				if (pGameEntity->GetCharacter(i))
					return pGameEntity->GetCharacter(i);
			}
		}
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::FindPhysicalObjectOwner( IPhysicalEntity *pPhysicalEntity )
{
	if (!pPhysicalEntity)
		return 0;

	int itype = pPhysicalEntity->GetiForeignData();
	switch (itype)
	{
	case PHYS_FOREIGN_ID_ROPE:
		{
			IRopeRenderNode *pRenderNode = (IRopeRenderNode*)pPhysicalEntity->GetForeignData(itype);
			if (pRenderNode)
			{
				EntityId id = (EntityId)pRenderNode->GetEntityOwner();
				CEntityObject *pEntity = CEntityObject::FindFromEntityId(id);
				return pEntity;
			}
		}
		break;
	case PHYS_FOREIGN_ID_ENTITY:
		{
			IEntity *pIEntity = gEnv->pEntitySystem->GetEntityFromPhysics(pPhysicalEntity);
			if (pIEntity)
			{
				return CEntityObject::FindFromEntityId(pIEntity->GetId());
			}
		}
		break;
	case PHYS_FOREIGN_ID_STATIC:
		{
			IRopeRenderNode *pRenderNode = (IRopeRenderNode*)pPhysicalEntity->GetForeignData(itype);
			if (pRenderNode)
			{
				// Find brush who created this render node.

			}
		}
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::OnObjectModified( CBaseObject *pObject,bool bDelete,bool boModifiedTransformOnly )
{
	if (!m_bLoadingObjects)
	{
		if (!GetIEditor()->IsUndoSuspended())
		{
			// When not doing objects loading... for now
			GetIEditor()->GetConsoleSync()->OnObjectModified( pObject,bDelete,boModifiedTransformOnly );
			if(bDelete)
			{
				SW_ON_OBJ_DEL(pObject);
			}
			else
				SW_ON_OBJ_MOD(pObject);
		}

		if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			if (pObject->IsKindOf(RUNTIME_CLASS(CAITerritoryObject)) || pObject->IsKindOf(RUNTIME_CLASS(CAIWaveObject)))
			{
				RefreshEntitiesAssignedToSelectedTnW();
			}
			else
			{
				CEntityObject* pEntity = static_cast<CEntityObject*>(pObject);	// Editor's class CEntity
				IEntity* pIEntity = pEntity->GetIEntity();	// CryEntitySystem's interface IEntity
				if (pIEntity)
				{
					IAIObject* pAIObject = pIEntity->GetAI();
					if (pAIObject && pAIObject->IsAgent())
					{
						RefreshEntitiesAssignedToSelectedTnW();
					}
				}
			}
		}
	}
}


//////////////////////////////////////////////////////////////////////////
void CObjectManager::UnregisterNoExported()
{
	I3DEngine * p3DEngine = GetIEditor()->Get3DEngine();
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject * pObj = it->second;
		CObjectLayer * pLayer = pObj->GetLayer();
		if(pLayer && !pLayer->IsExportable())
		{
			IRenderNode * pRenderNode = pObj->GetEngineNode();
			if(pRenderNode && pRenderNode->GetEntityStatObj())
				p3DEngine->UnRegisterEntity( pRenderNode );
		}
	}
}


//////////////////////////////////////////////////////////////////////////
void CObjectManager::RegisterNoExported()
{
	I3DEngine * p3DEngine = GetIEditor()->Get3DEngine();
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject * pObj = it->second;
		CObjectLayer * pLayer = pObj->GetLayer();
		if(pLayer && !pLayer->IsExportable())
		{
			IRenderNode * pRenderNode = pObj->GetEngineNode();
			if(pRenderNode && pRenderNode->GetEntityStatObj())
				p3DEngine->RegisterEntity( pRenderNode );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::IsEntityAssignedToSelectedTerritory( CEntityObject* pEntity )
{
	return m_setEntitiesAssignedToSelectedTerritory.find(pEntity) != m_setEntitiesAssignedToSelectedTerritory.end();
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::IsEntityAssignedToSelectedWave( CEntityObject* pEntity )
{
	return m_setEntitiesAssignedToSelectedWave.find(pEntity) != m_setEntitiesAssignedToSelectedWave.end();
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::IsLightClass( CBaseObject* pObject )
{
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = (CEntityObject*)pObject;
		if (pEntity)				
		{
			if (pEntity->GetEntityClass().Compare(CLASS_LIGHT)==0 )
				return TRUE;
			if (pEntity->GetEntityClass().Compare(CLASS_RIGIDBODY_LIGHT)==0 )
				return TRUE;
			if (pEntity->GetEntityClass().Compare(CLASS_DESTROYABLE_LIGHT)==0 )
				return TRUE;
		}
	}

	return FALSE;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RefreshEntitiesAssignedToSelectedTnW()
{
	m_setEntitiesAssignedToSelectedTerritory.clear();
	m_setEntitiesAssignedToSelectedWave.clear();
	
	std::vector<CAITerritoryObject*> vSelectedTerritories;
	std::set<CString> setSelectedTerritories;
	std::set<CString> setSelectedWaves;
	
	std::map<CEntityObject*, CString> mapEntityTerritories;
	std::vector<CEntityObject*> vEntitiesWithAutoAssignment;
	std::map<CEntityObject*, CString> mapEntityWaves;
	
	CString sEntityTerritory;
	CString sEntityWave;

	CBaseObjectsArray objects;
	GetObjects(objects);

	// First clarify relationships between Entities (e.g. Grunts) and AI Territories & Waves

	for (size_t i = 0, n = objects.size(); i < n; ++i)
	{
		CBaseObject* pObject = objects[i];
		if (!pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			continue;
		}
		
		CEntityObject* pEntity = static_cast<CEntityObject*>(pObject);

		if (pEntity->IsHidden() || pEntity->IsHiddenBySpec() || pEntity->IsFrozen())
		{
			continue;
		}
		
		if (pEntity->IsKindOf(RUNTIME_CLASS(CAITerritoryObject)))
		{
			if (pEntity->IsSelected())
			{
				vSelectedTerritories.push_back(static_cast<CAITerritoryObject*>(pEntity));
				setSelectedTerritories.insert(pEntity->GetName());
			}
		}
		else if (pEntity->IsKindOf(RUNTIME_CLASS(CAIWaveObject)))
		{
			if (pEntity->IsSelected())
			{
				setSelectedWaves.insert(pEntity->GetName());
			}
		}
		else
		{
			// Associate Entities (e.g. Grunts) with their Territories and Waves
		
			CVarBlock* pProperties2 = pEntity->GetProperties2();
			if (pProperties2)
			{
				IVariable* pVarTerritory = pProperties2->FindVariable("aiterritory_Territory");
				if (pVarTerritory)
				{
					pVarTerritory->Get(sEntityTerritory);
					if (!sEntityTerritory.IsEmpty() && (sEntityTerritory != "<None>"))
					{
						if (sEntityTerritory == "<Auto>")
						{
							vEntitiesWithAutoAssignment.push_back(pEntity);
						}
						else
						{
							mapEntityTerritories.insert(std::make_pair(pEntity, sEntityTerritory));
							
							IVariable* pVarWave = pProperties2->FindVariable("aiwave_Wave");
							if (pVarWave)
							{
								pVarWave->Get(sEntityWave);
								if (!sEntityWave.IsEmpty() && (sEntityWave != "<None>"))
								{
									mapEntityWaves.insert(std::make_pair(pEntity, sEntityWave));
								}
							}
						}
					}
				}
			}
		}
	}

	// Now figure out what to select

	for (std::map<CEntityObject*, CString>::iterator it = mapEntityTerritories.begin(), end = mapEntityTerritories.end(); it != end; ++it)
	{
		CEntityObject* pEntity = it->first;
		const CString& sTerritoryName = it->second;

		if (setSelectedTerritories.find(sTerritoryName) != setSelectedTerritories.end())
		{
			m_setEntitiesAssignedToSelectedTerritory.insert(pEntity);
		}
	}
		
	for (std::vector<CAITerritoryObject*>::iterator it = vSelectedTerritories.begin(), end = vSelectedTerritories.end(); it != end; ++it)
	{
		CAITerritoryObject* territory = *it;
		
		const Matrix34& tm = territory->GetWorldTM();
		std::vector<Vec3> territoryPoints;
		for (int i = 0, n = territory->GetPointCount(); i < n; ++i)
		{
			Vec3 point = tm.TransformPoint(territory->GetPoint(i));
			territoryPoints.push_back(point);
		}

		for (std::vector<CEntityObject*>::iterator it2 = vEntitiesWithAutoAssignment.begin(), end = vEntitiesWithAutoAssignment.end(); it2 != end; ++it2)
		{
			CEntityObject* pEntity = *it2;

			const Vec3& pos = pEntity->GetPos();
			float height = territory->GetHeight();
			float h = pos.z - territory->GetPos().z;
			if ((height < 0.01f) || ((0.0f < h) && (h < height)))
			{
				if (Overlap::Point_Polygon2D(pos, territoryPoints))
				{
					m_setEntitiesAssignedToSelectedTerritory.insert(pEntity);
				}
			}
		}
	}
		
	for (std::map<CEntityObject*, CString>::iterator it = mapEntityWaves.begin(), end = mapEntityWaves.end(); it != end; ++it)
	{
		CEntityObject* pEntity = it->first;
		const CString& sWaveName = it->second;

		if (setSelectedWaves.find(sWaveName) != setSelectedWaves.end())
		{
			m_setEntitiesAssignedToSelectedWave.insert(pEntity);
		}
	}
}

size_t CObjectManager::NumberOfAssignedEntities()
{
	std::set<CEntityObject*> result;
	std::set_union(
		m_setEntitiesAssignedToSelectedTerritory.begin(), m_setEntitiesAssignedToSelectedTerritory.end(),
		m_setEntitiesAssignedToSelectedWave.begin(), m_setEntitiesAssignedToSelectedWave.end(),
		inserter(result, result.begin()));
	return result.size();
}

void CObjectManager::SelectAssignedEntities()
{
	// Memorize what to select before call to ClearSelection()
	std::set<CEntityObject*> t = m_setEntitiesAssignedToSelectedTerritory;
	std::set<CEntityObject*> w = m_setEntitiesAssignedToSelectedWave;
	
	ClearSelection();
	
	SelectEntities(t);
	SelectEntities(w);
}

void CObjectManager::FindAndRenameProperty2(const char* property2Name, const CString& oldValue, const CString& newValue)
{
	CBaseObjectsArray objects;
	GetObjects(objects);

	for (size_t i = 0, n = objects.size(); i < n; ++i)
	{
		CBaseObject* pObject = objects[i];
		if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			CEntityObject* pEntity = static_cast<CEntityObject*>(pObject);
			CVarBlock* pProperties2 = pEntity->GetProperties2();
			if (pProperties2)
			{
				IVariable* pVariable = pProperties2->FindVariable(property2Name);
				if (pVariable)
				{
					CString sValue;
					pVariable->Get(sValue);
					if (sValue == oldValue)
					{
						pEntity->StoreUndo("Rename Property2");
					
						pVariable->Set(newValue);

						// Special case
						if ((property2Name == "aiterritory_Territory") && ((newValue == "<Auto>") || (newValue == "<None>") || (newValue != oldValue)))
						{
							IVariable* pVariableWave = pProperties2->FindVariable("aiwave_Wave");
							if (pVariableWave)
							{
								pVariableWave->Set("<None>");
							}
						}
					}
				}
			}
		}
	}
}

void CObjectManager::FindAndRenameProperty2If(const char* property2Name, const CString& oldValue, const CString& newValue, const char* otherProperty2Name, const CString& otherValue)
{
	CBaseObjectsArray objects;
	GetObjects(objects);

	for (size_t i = 0, n = objects.size(); i < n; ++i)
	{
		CBaseObject* pObject = objects[i];
		if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			CEntityObject* pEntity = static_cast<CEntityObject*>(pObject);
			CVarBlock* pProperties2 = pEntity->GetProperties2();
			if (pProperties2)
			{
				IVariable* pVariable      = pProperties2->FindVariable(property2Name);
				IVariable* pOtherVariable = pProperties2->FindVariable(otherProperty2Name);
				if (pVariable && pOtherVariable)
				{
					CString sValue;
					pVariable->Get(sValue);

					CString sOtherValue;
					pOtherVariable->Get(sOtherValue);
					
					if ((sValue == oldValue) && (sOtherValue == otherValue))
					{
						pEntity->StoreUndo("Rename Property2 If");

						pVariable->Set(newValue);

						// Special case
						if ((property2Name == "aiterritory_Territory") && ((newValue == "<Auto>") || (newValue == "<None>")))
						{
							IVariable* pVariableWave = pProperties2->FindVariable("aiwave_Wave");
							if (pVariableWave)
							{
								pVariableWave->Set("<None>");
							}
						}
					}
				}
			}
		}
	}
}


void CObjectManager::ResolveMissingObjects()
{
	enum{
		eInit = 0,
		eNoForAll = 1,
		eNo = 2,
		eYesForAll = 3,
		eYes = 4
	};

	typedef std::map<CString,CString> LocationMap;
	LocationMap locationMap;

	int locationState = eInit;
	bool isUpdated = false;

	Log("Resolving missed objects...");

	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject* obj = it->second;

		CGeomEntity* pGeomEntity = 0;
		CSimpleEntity* pSimpleEntity = 0;
		CBrushObject* pBrush = 0;
		IEntity* pEntity = 0; 
		CString geometryFile;
		IVariable* pModelVar = 0;

		if (obj->IsKindOf(RUNTIME_CLASS(CGeomEntity)))
		{
			IEntity* pEntityObj = ((CGeomEntity*)obj)->GetIEntity();
			if(pEntityObj && pEntityObj->GetStatObj(0) && pEntityObj->GetStatObj(0)->IsDefaultObject())
			{
				pGeomEntity = (CGeomEntity*)obj;
				geometryFile = pGeomEntity->GetGeometryFile();
			}
		}
		else if (obj->IsKindOf(RUNTIME_CLASS(CSimpleEntity)))
		{
			IEntity* pEntityObj = ((CSimpleEntity*)obj)->GetIEntity();
			if(pEntityObj && pEntityObj->GetStatObj(0) && pEntityObj->GetStatObj(0)->IsDefaultObject())
			{
				pSimpleEntity = (CSimpleEntity*)obj;
				geometryFile = pSimpleEntity->GetGeometryFile();
			}
		}
		else if (obj->IsKindOf(RUNTIME_CLASS(CBrushObject)))
		{
			CBrushObject* pBrushObj = (CBrushObject*)obj;
			if(pBrushObj->GetGeometry() && ((CEdMesh*)pBrushObj->GetGeometry())->IsDefaultObject())
			{
				pBrush = (CBrushObject*)obj;
				geometryFile = pBrush->GetGeometryFile();
			}
		}
		else if (obj->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			IEntity* pEntityObj = ((CEntityObject*)obj)->GetIEntity();
			if(pEntityObj && pEntityObj->GetStatObj(0) && pEntityObj->GetStatObj(0)->IsDefaultObject())
			{
				CVarBlock* pVars = ((CEntityObject*)obj)->GetProperties();
				if(pVars)
				{
					for(int i=0; i<pVars->GetVarsCount(); i++)
					{
						pModelVar = pVars->GetVariable(i);
						if(pModelVar && pModelVar->GetDataType()==IVariable::DT_FILE)
						{
							pModelVar->Get( geometryFile );
							const char *ext = PathUtil::GetExt(geometryFile);
							if (stricmp(ext,CRY_GEOMETRY_FILE_EXT) == 0 || stricmp(ext,CRY_CHARACTER_FILE_EXT) == 0 || stricmp(ext,CRY_CHARACTER_DEFINITION_FILE_EXT) == 0 || stricmp(ext,CRY_ANIM_GEOMETRY_FILE_EXT) == 0 || stricmp(ext,CRY_CHARACTER_PARTS_FILE_EXT) == 0)
							{
								if(!gEnv->pCryPak->IsFileExist(geometryFile))
								{
									pEntity = pEntityObj;
									break;
								}
							}
						}
					}
				}
			}
		}

		if(!pGeomEntity && !pSimpleEntity && !pBrush && !pEntity)
			continue;

		int nKey=0;

		CString newFilename = stl::find_in_map( locationMap, geometryFile, CString("") );
		if(newFilename!="")
		{
			if(pGeomEntity)
			{
				pGeomEntity->SetGeometryFile(newFilename);
			}
			else if(pSimpleEntity)
			{
				pSimpleEntity->SetGeometryFile(newFilename);
			}
			else if(pBrush)
			{
				pBrush->CreateBrushFromPrefab(newFilename);
				if(pBrush->GetGeometry() && !((CEdMesh*)pBrush->GetGeometry())->IsDefaultObject())
				{
					pBrush->SetGeometryFile(newFilename);
				}
			}
			else if(pEntity)
			{
				pModelVar->Set(newFilename);
			}
			Log("%s: %s <- %s", obj->GetName(), geometryFile, newFilename);
			continue;
		}

		if(locationState==eNoForAll)
			continue;

		if(locationState!=eYesForAll) // Skip, if "Yes for All" pressed before
		{
			CString mes;
			mes.Format( _T("Geometry file for object \"%s\" is missing/removed. \r\nFile: %s\r\nAttempt to locate this file?"), (const char*)obj->GetName(), (const char*)geometryFile);
			nKey = CCustomMessageBox::Show(mes,_T("Object missing"), _T("No for All"), _T("No"), _T("Yes for All"), _T("Yes") );

			if(nKey==eNoForAll && locationMap.size()==0)
				break;

			if(nKey==eNoForAll || nKey==eYesForAll)
				locationState = nKey;
		}

		if(nKey==eYes || locationState==eYesForAll)
		{
			CFileUtil::FileArray cFiles;
			CString filemask = PathUtil::GetFile(geometryFile);
			CFileUtil::ScanDirectory(PathUtil::GetGameFolder().c_str(),filemask,cFiles,true);

			if(cFiles.size())
			{
				CString newFilename = cFiles[0].filename;
				if(pGeomEntity)
				{
					pGeomEntity->SetGeometryFile(newFilename);
				}
				else if(pSimpleEntity)
				{
					pSimpleEntity->SetGeometryFile(newFilename);
				}
				else if(pBrush)
				{
					pBrush->CreateBrushFromPrefab(newFilename);
					if(pBrush->GetGeometry() && !((CEdMesh*)pBrush->GetGeometry())->IsDefaultObject())
					{
						pBrush->SetGeometryFile(newFilename);
					}
				}
				else if(pEntity)
				{
					pModelVar->Set(newFilename);
				}
				locationMap[geometryFile] = newFilename;
				Log("%s: %s <- %s", obj->GetName(), geometryFile, newFilename);
				isUpdated = true;
			}
			else
				GetIEditor()->GetSystem()->GetILog()->LogWarning("Can't resolve object: %s: %s", obj->GetName(), geometryFile);
		}
	}
	if(isUpdated)
		GetIEditor()->SetModifiedFlag();
	else
		Log("No objects has been resolved.");

	ResolveMissingMaterials();
}


void CObjectManager::ResolveMissingMaterials()
{
	enum{
		eInit = 0,
		eNoForAll = 1,
		eNo = 2,
		eYesForAll = 3,
		eYes = 4
	};

	typedef std::map<CString,CString> LocationMap;
	LocationMap locationMap;

	int locationState = eInit;
	bool isUpdated = false;

	CString oldFilename;

	Log("Resolving missed materials...");

	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject* obj = it->second;

		CMaterial* pMat = obj->GetMaterial();

		if(pMat && pMat->GetMatInfo() && pMat->GetMatInfo()->IsDefault())
		{
			oldFilename = pMat->GetFilename();
		}
		else
			continue;

		int nKey=0;

		CString newFilename = stl::find_in_map( locationMap, oldFilename, CString("") );
		if(newFilename!="")
		{
			CMaterial* pNewMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial( newFilename );
			if(pNewMaterial)
			{
				obj->SetMaterial(pNewMaterial);
				Log("%s: %s <- %s", pMat->GetName(), oldFilename, newFilename);
			}
			continue;
		}

		if(locationState==eNoForAll)
			continue;

		if(locationState!=eYesForAll) // Skip, if "Yes for All" pressed before
		{
			CString mes;
			mes.Format( _T("Material for object \"%s\" is missing/removed. \r\nFile: %s\r\nAttempt to locate this file?"), (const char*)obj->GetName(), (const char*)oldFilename);
			nKey = CCustomMessageBox::Show(mes,_T("Material missing"), _T("No for All"), _T("No"), _T("Yes for All"), _T("Yes") );

			if(nKey==eNoForAll && locationMap.size()==0)
				break;

			if(nKey==eNoForAll || nKey==eYesForAll)
				locationState = nKey;
		}

		if(nKey==eYes || locationState==eYesForAll)
		{
			CFileUtil::FileArray cFiles;
			CString filemask = PathUtil::GetFile(oldFilename);
			CFileUtil::ScanDirectory(PathUtil::GetGameFolder().c_str(),filemask,cFiles,true);

			if(cFiles.size())
			{
				CString newFilename = cFiles[0].filename;

				CMaterial* pNewMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial( newFilename );
				if(pNewMaterial)
				{
					obj->SetMaterial(pNewMaterial);
					locationMap[oldFilename] = newFilename;
					Log("%s: %s <- %s", pMat->GetName(), oldFilename, newFilename);
					isUpdated = true;
				}
			}
			else
				GetIEditor()->GetSystem()->GetILog()->LogWarning("Can't resolve material: %s: %s", pMat->GetName(), oldFilename);
		}
		
	}
	if(isUpdated)
		GetIEditor()->SetModifiedFlag();
	else
		Log("No materials has been resolved.");
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UpdateBoneAttachedEntities()
{
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		if (obj->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			CEntityObject* pEntity = static_cast<CEntityObject*>(obj);
			if (pEntity->GetBoneAttachLinkIndex() >= 0)
				pEntity->SetWorldTM(pEntity->GetIEntity()->GetWorldTM(), TM_RESTORE_UNDO);
		}
	}
}
